﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PPC_emulator
{
    class Processor
    {
        BPList bplist;
        public Processor(BPList bplist)
        {
            this.bplist = bplist;
        }
        public Registers registers = new Registers();
        OPDecipherer opd = new OPDecipherer();
        public RAM ram = new RAM();

        public ErrorCode process(String OP)
        {
            try
            {
                if (!opd.isValid(OP))
                    return ErrorCode.NoSpace;
                switch (opd.getOPC(OP))
                {
                    case OPCodes.li:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            UInt16 value = (UInt16)opd.getValue(OP);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.lis:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            UInt16 value = (UInt16)opd.getValue(OP);
                            UInt32 result = UInt32.Parse(value.ToString("X4") + "0000", System.Globalization.NumberStyles.AllowHexSpecifier);
                            registers.setRegister(rD, result);
                            bplist.setRegister(rD, result);
                            break;
                        }

                    case OPCodes.addi:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 value = registers.getRegister(rA);
                            value += (UInt16)opd.getValue2(OP);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.add:
                        {
                            int rD = opd.getrD(OP); 
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            int rB = opd.getrB(OP);
                            if (rB == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 value = registers.getRegister(rA) + registers.getRegister(rB);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.addis:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 result = registers.getRegister(rA);
                            UInt32 value = (UInt16)opd.getValue2(OP);
                            result += value << 16;
                            registers.setRegister(rD, result);
                            bplist.setRegister(rD, result);
                            break;
                        }

                    case OPCodes.and:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            int rB = opd.getrB(OP);
                            if (rB == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 value = registers.getRegister(rA) & registers.getRegister(rB);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.andi:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 value = registers.getRegister(rA);
                            value &= (UInt16)opd.getValue2(OP);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.andis:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 result = registers.getRegister(rA);
                            UInt32 value = (UInt16)opd.getValue2(OP);
                            result &= value << 16;
                            registers.setRegister(rD, result);
                            bplist.setRegister(rD, result);
                            break;
                        }

                    case OPCodes.mr:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            registers.setRegister(rD, registers.getRegister(rA));
                            bplist.setRegister(rD, registers.getRegister(rA));
                            break;
                        }

                    case OPCodes.not:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 value = ~registers.getRegister(rA);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.ori:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 value = registers.getRegister(rA);
                            value |= (UInt16)opd.getValue2(OP);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.oris:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 result = registers.getRegister(rA);
                            UInt32 value = (UInt16)opd.getValue2(OP);
                            result |= value << 16;
                            registers.setRegister(rD, result);
                            bplist.setRegister(rD, result);
                            break;
                        }

                    case OPCodes.slwi:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 result = registers.getRegister(rA);
                            UInt16 value = (UInt16)opd.getValue2(OP);
                            result = result << value;
                            registers.setRegister(rD, result);
                            bplist.setRegister(rD, result);
                            break;
                        }

                    case OPCodes.srwi:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 result = registers.getRegister(rA);
                            UInt16 value = (UInt16)opd.getValue2(OP);
                            result = result >> value;
                            registers.setRegister(rD, result);
                            bplist.setRegister(rD, result);
                            break;
                        }

                    case OPCodes.subi:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 value = registers.getRegister(rA);
                            value -= (UInt16)opd.getValue2(OP);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.sub:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            int rB = opd.getrB(OP);
                            if (rB == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 value = registers.getRegister(rA) - registers.getRegister(rB);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.subis:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            int rA = opd.getrA(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 result = registers.getRegister(rA);
                            UInt32 value = (UInt16)opd.getValue2(OP);
                            result -= value << 16;
                            registers.setRegister(rD, result);
                            bplist.setRegister(rD, result);
                            break;
                        }

                    case OPCodes.lwz:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            UInt16 d = opd.getd(OP);
                            int rA = opd.getrA2(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 address = registers.getRegister(rA) + d;
                            if ((address < 2147483648) || (address > 2172649472))
                                return ErrorCode.InvalidAddress;
                            UInt32 value = get32RAM(address);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.stw:
                        {
                            int rS = opd.getrD(OP);
                            if (rS == -1)
                                return ErrorCode.InvalidRegister;
                            UInt16 d = opd.getd(OP);
                            int rA = opd.getrA2(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 address = registers.getRegister(rA) + d;
                            if ((address < 2147483648) || (address > 2172649472))
                                return ErrorCode.InvalidAddress;
                            UInt32 value = registers.getRegister(rS);
                            write32RAM(address, value);
                            break;
                        }

                    case OPCodes.lhz:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            UInt16 d = opd.getd(OP);
                            int rA = opd.getrA2(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 address = registers.getRegister(rA) + d;
                            if ((address < 2147483648) || (address > 2172649472))
                                return ErrorCode.InvalidAddress;
                            UInt16 value = get16RAM(address);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.sth:
                        {
                            int rS = opd.getrD(OP);
                            if (rS == -1)
                                return ErrorCode.InvalidRegister;
                            UInt16 d = opd.getd(OP);
                            int rA = opd.getrA2(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 address = registers.getRegister(rA) + d;
                            if ((address < 2147483648) || (address > 2172649472))
                                return ErrorCode.InvalidAddress;
                            UInt16 value = (UInt16)registers.getRegister(rS);
                            write16RAM(address, value);
                            break;
                        }

                    case OPCodes.lbz:
                        {
                            int rD = opd.getrD(OP);
                            if (rD == -1)
                                return ErrorCode.InvalidRegister;
                            UInt16 d = opd.getd(OP);
                            int rA = opd.getrA2(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 address = registers.getRegister(rA) + d;
                            if ((address < 2147483648) || (address > 2172649472))
                                return ErrorCode.InvalidAddress;
                            byte value = get8RAM(address);
                            registers.setRegister(rD, value);
                            bplist.setRegister(rD, value);
                            break;
                        }

                    case OPCodes.stb:
                        {
                            int rS = opd.getrD(OP);
                            if (rS == -1)
                                return ErrorCode.InvalidRegister;
                            UInt16 d = opd.getd(OP);
                            int rA = opd.getrA2(OP);
                            if (rA == -1)
                                return ErrorCode.InvalidRegister;
                            UInt32 address = registers.getRegister(rA) + d;
                            if ((address < 2147483648) || (address > 2172649472))
                                return ErrorCode.InvalidAddress;
                            byte value = (byte)registers.getRegister(rS);
                            write8RAM(address, value);
                            break;
                        }

                    default:
                        {
                            return ErrorCode.OPCNotFound;
                        }
                }
                return ErrorCode.Succes;
            }
            catch(Exception e)
            {
                Console.Write(e);
                return ErrorCode.Unknown;
            }
        }

        public UInt32 get32RAM(UInt32 address)
        {
            UInt32 value = ram.getValue32(address - 2147483648);
            return value;
        }

        public void write32RAM(UInt32 address, UInt32 value)
        {
            ram.setAddress32(address - 2147483648, value);
        }

        public UInt16 get16RAM(UInt32 address)
        {
            UInt16 value = ram.getValue16(address - 2147483648);
            return value;
        }

        public void write16RAM(UInt32 address, UInt16 value)
        {
            ram.setAddress16(address - 2147483648, value);
        }

        public byte get8RAM(UInt32 address)
        {
            byte value = ram.getValue8(address - 2147483648);
            return value;
        }

        public void write8RAM(UInt32 address, byte value)
        {
            ram.setAddress8(address - 2147483648, value);
        }
    }
}
